﻿using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;
using Core.Log;
using FrameWork.Context.Oprator;
using FrameWork.Util.Extension;

namespace FrameWork.Context
{
    /// <summary>
    /// 操作数据库基类
    /// </summary>
    public class DbContextBase : DbContext
    {
        /// <summary>
        /// 构造database
        /// </summary>
        /// <param name="connectionString">数据库连接字符串</param>
        public DbContextBase(string connectionString)
        {
            Database.Connection.ConnectionString = connectionString;
            Configuration.LazyLoadingEnabled = false;
            Configuration.ProxyCreationEnabled = false;
        }

        /// <summary>
        /// 构造database并添加操作记录类
        /// </summary>
        /// <param name="connectionString">数据库连接字符串</param>
        /// <param name="auditLogger">操作记录类</param>
        public DbContextBase(string connectionString, IAuditable auditLogger)
            : this(connectionString)
        {
            AuditLogger = auditLogger;
        }

        /// <summary>
        /// 操作记录类
        /// </summary>
        public IAuditable AuditLogger { get; set; }

        /// <summary>
        /// 更新数据库记录
        /// </summary>
        public T Update<T>(T entity) where T : ModelBase
        {
            var set = Set<T>();
            set.Attach(entity);
            Entry<T>(entity).State = EntityState.Modified;
            SaveChanges();

            return entity;
        }

        /// <summary>
        /// 生成更新实体的sql但不操作数据库
        /// </summary>
        public T UpdateNotSaveChange<T>(T entity) where T : ModelBase
        {
            var set = Set<T>();
            set.Attach(entity);
            foreach (var p in entity.GetType().GetProperties())
            {
                if (p.GetValue(entity) != null)
                {
                    try
                    {
                        Entry(entity).Property(p.Name).IsModified = true;
                    }
                    catch (Exception)
                    {
                        continue;
                    }
                }
            }
            return entity;
        }

        /// <summary>
        /// 插入到数据库
        /// </summary>
        public T Insert<T>(T entity) where T : ModelBase
        {
            Set<T>().Add(entity);
            SaveChanges();
            return entity;
        }

        /// <summary>
        /// 批量插入数据
        /// </summary>
        public int BatchInsert<T>(List<T> list) where T : ModelBase
        {
            Set<T>().AddRange(list);
            SaveChanges();
            return list.Count;
        }

        /// <summary>
        /// 生成插入实体的sql但不操作数据库
        /// </summary>
        public T InsertNotSaveChange<T>(T entity) where T : ModelBase
        {
            Set<T>().Add(entity);
            return entity;
        }

        /// <summary>
        /// 从数据库中删除对应的实体
        /// </summary>
        public void Delete<T>(T entity) where T : ModelBase
        {
            Entry<T>(entity).State = EntityState.Deleted;
            SaveChanges();
        }

        /// <summary>
        /// 根据主键获取对应的实体，如果找不到就返回null
        /// </summary>
        public T Find<T>(params object[] keyValues) where T : ModelBase
        {
            return Set<T>().Find(keyValues);
        }

        /// <summary>
        /// 根据兰姆达表达式获取对应的列表
        /// </summary>
        public List<T> FindAll<T>(Expression<Func<T, bool>> conditions = null) where T : ModelBase
        {
            if (conditions == null)
                return Set<T>().ToList();
            return Set<T>().Where(conditions).ToList();
        }

        /// <summary>
        /// 执行到数据库
        /// </summary>
        public override int SaveChanges()
        {
            WriteAuditLog();

            var result = base.SaveChanges();
            return result;
        }

        /// <summary>
        /// 操作记录
        /// </summary>
        internal void WriteAuditLog()
        {
            if (this.AuditLogger == null)
                return;

            foreach (var dbEntry in this.ChangeTracker.Entries<ModelBase>().Where(p => p.State == EntityState.Added || p.State == EntityState.Deleted || p.State == EntityState.Modified))
            {
                var opModel = OperatorProvider.Provider.GetCurrent();
                var operaterName = string.Empty;
                if (opModel != null)
                    operaterName = opModel.Account;
                var t = Task.Factory.StartNew(() =>
                {
                    var tableName = dbEntry.Entity.GetType().GetCustomAttributes(typeof(TableAttribute), false)
                        .SingleOrDefault() is TableAttribute tableAttr ? tableAttr.Name : dbEntry.Entity.GetType().Name;
                    var moduleName = dbEntry.Entity.GetType().FullName.Split('.').Skip(1).FirstOrDefault();
                    AuditLogger.WriteLog(dbEntry.Entity.Id, operaterName, moduleName, tableName, dbEntry.State.ToString(), dbEntry.Entity.ToJson());
                });
                t.Wait();
            }
        }
    }
}
